package lumis.doui.source.field.type;

import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

import lumis.doui.ExtractedText;
import lumis.doui.render.IRenderable;
import lumis.doui.render.IRenderer;
import lumis.doui.search.common.ISearchable;
import lumis.portal.PortalException;
import lumis.portal.authentication.SessionConfig;
import lumis.portal.file.FileConfig;
import lumis.portal.file.FileDownloadControllerHtml;
import lumis.portal.file.FileMimeTypeConfig;
import lumis.portal.file.FilesConfig;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.transaction.PortalTransactionFactory;
import lumis.search.SearchContentAttachment;
import lumis.search.SearchContentField;
import lumis.util.FileUtil;
import lumis.util.ITransaction;
import lumis.util.XmlUtil;
import lumis.util.converter.AbstractConverter;
import lumis.util.converter.ConversionException;
import lumis.util.converter.IConverter;
import lumis.util.converter.UnsupportedConversionTypeException;
import lumis.util.log.ILogger;
import lumis.util.log.LoggerFactory;
import lumis.util.query.QueryFieldTypes;

/**
 * A files data type. Its value is a list of files stored.
 * 
 * @version $Revision: 10161 $ $Date: 2009-02-26 18:14:04 -0300 (Thu, 26 Feb 2009) $
 * @since 4.0.4
 */
public class FilesDataType extends AbstractSourceFieldDataType
{
	protected static class FilesDataConverter extends AbstractConverter
	{
		@SuppressWarnings("unchecked")
		@Override
		protected <T> T doConversion(Class<T> type, Object value, Locale locale, String pattern) throws UnsupportedConversionTypeException
		{
			// get filesId
			String filesId;
			if (value instanceof FilesConfig)
				filesId = ((FilesConfig)value).getId();
			else
				filesId = value.toString();
			
			if (type.isAssignableFrom(Data.class))
			{
				FilesConfig filesConfig;
				try
				{
					filesConfig = ManagerFactory.getFileManager().getFilesConfig(
							SessionConfig.getCurrentSessionConfig(), filesId, 
							PortalTransactionFactory.getCurrentTransaction());
					if (filesConfig == null)
					{
						// the id should be a representation of a not yet inserted
						// filesConfig (it is inserted only when a file is added).
						// So create a filesConfig object directly to represent it.
						filesConfig = new FilesConfig();
						filesConfig.setId(filesId);
					}
					return (T)new Data(filesConfig);
				}
				catch (PortalException e)
				{
					throw new ConversionException(e);
				}
			}
			else if (type.isAssignableFrom(ExtractedText.class))
			{
				return (T)new ExtractedText(FilesDataParser.extract(
						SessionConfig.getCurrentSessionConfig(),
						filesId, PortalTransactionFactory.getCurrentTransaction()));
			}
			else if (type.isAssignableFrom(String.class))
				return (T)filesId;
			else
				throw new UnsupportedConversionTypeException();
		}	
	}

	protected static class FilesDataParser
	{
		private static ILogger logger = LoggerFactory.getLogger(FilesDataParser.class);

		public static String extract(SessionConfig sessionConfig, String filesId, ITransaction transaction)
		{
			StringBuilder text = new StringBuilder();
			try
			{
				Collection<FileConfig> fileConfigs = ManagerFactory.getFileManager().getFileConfigsByFilesId(sessionConfig, filesId, transaction);				
				for (FileConfig fileConfig : fileConfigs)
				{
					text.append(FileDataType.FileDataParser.extract(sessionConfig, fileConfig, transaction));
					text.append(" ");
				}
			}
			catch (Exception e)
			{
				logger.debug("Error parsing files ", e);
			}
			return text.toString();
		}
}
	/**
	 * Data object for a field with data type files.
	 * @since 4.0.8
	 */
	public static class Data extends FilesConfig implements IRenderable, ISearchable
	{
		public Data(FilesConfig filesConfig)
		{
			this.setId(filesConfig.getId());
		}
		
		public String getRenderData(IRenderer renderer) throws PortalException
		{
			SessionConfig sessionConfig = renderer.getSourceContext().getSessionConfig();
			ITransaction transaction = renderer.getSourceContext().getTransaction();
			Collection<FileConfig> fileConfigs = ManagerFactory.getFileManager().getFileConfigsByFilesId(
					sessionConfig, getId(), transaction);
			StringBuilder value = new StringBuilder(1000);
			value.append("<files id=\"" + XmlUtil.encodeXml(getId()) + "\">");
			for (FileConfig fileConfig: fileConfigs)
			{
				value.append("<file>");
				
				// get file's mime type
				FileMimeTypeConfig fileMimeTypeConfig;
				String contentType = fileConfig.getContentType();
				if (contentType != null)
					fileMimeTypeConfig = ManagerFactory.getFileManager().getMimeType(sessionConfig, contentType, transaction);
				else
					fileMimeTypeConfig = null;
				
				// generate xml for the file, using FileDataType.Data
				FileDataType.Data fileData = new FileDataType.Data(fileConfig, fileMimeTypeConfig);
				value.append(fileData.getRenderData(renderer));
				
				value.append("</file>");
			}
			value.append("</files>");
			return value.toString();
		}

		/**
		 * Returns the display string, as defined in {@link FileDataType.Data#getDisplayString(IRenderer)},
		 * of each file in this files data, separated by comma.
		 * @since 4.0.8
		 */
		public String getDisplayString(IRenderer renderer) throws PortalException
		{
			Collection<FileConfig> fileConfigs = ManagerFactory.getFileManager().getFileConfigsByFilesId(
					renderer.getSourceContext().getSessionConfig(), getId(), 
					renderer.getSourceContext().getTransaction());
			StringBuilder value = new StringBuilder(100);
			for (FileConfig fileConfig: fileConfigs)
			{
				if (value.length() > 0)
					value.append(", ");
				value.append(fileConfig.getName());
			}
			return value.toString();
		}

		public Collection<SearchContentAttachment> getSearchContentAttachments()
		{
			// get all fileConfigs
			Collection<FileConfig> fileConfigs = null;
			try
			{
				fileConfigs = ManagerFactory.getFileManager().getFileConfigsByFilesId(
						SessionConfig.getCurrentSessionConfig(), getId(), PortalTransactionFactory.getCurrentTransaction());
			}
			catch (Exception e) 
			{
				throw new RuntimeException("Could not get fileConfigs for filesId: " + getId(), e);
			}
			
			// create attachment for each file config
			Collection<SearchContentAttachment> attachments = new ArrayList<SearchContentAttachment>();
			for(FileConfig fileConfig : fileConfigs)
			{
				SearchContentAttachment attachment = new SearchContentAttachment();
				attachment.setName(fileConfig.getName());
				attachment.setContentType(fileConfig.getContentType());
				attachment.setUrl(FileDownloadControllerHtml.getFileDownloadUrl(fileConfig.getId()).toString());
				try 
				{
					// get a file
					File file = ManagerFactory.getFileManager().getFile(SessionConfig.getCurrentSessionConfig(), fileConfig, PortalTransactionFactory.getCurrentTransaction());
					attachment.setContent(FileUtil.readFile(file));
					attachments.add(attachment);
				}
				catch (Exception e) 
				{
					throw new RuntimeException("Can not read file for fileConfig fileId=" + fileConfig.getId(), e);
				}
			}
			
			return attachments;
		}

		public List<SearchContentField> getSearchContentFields()
		{
			return Collections.emptyList();
		}		
	}
	
	@Override
	public String getId()
	{
		return "files";
	}
	
	@Override
	public String getQueryDataType()
	{
		return QueryFieldTypes.GUID;
	}

	@Override
	public String getDefaultInputControlType()
	{
		return "lum_filesPicker";
	}

	@Override
	protected IConverter createConverter()
	{
		return new FilesDataConverter();
	}

	@Override
	public Class<?> getValueClass()
	{
		return Data.class;
	}
}
